package com.senyint.sys.utils.config;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import com.senyint.sys.utils.io.IOUtil;
import com.senyint.sys.utils.log.Log;

public class XMLUtil {

	private static final String DATEFORMAT = "yyyy-MM-dd HH:mm:ss.SSS";

	public static Document obj2xml(Object obj, String dateFormat) {
		Document doc = DocumentHelper.createDocument();
		// doc.setXMLEncoding("utf-8");
		Element root = doc.addElement("root");
		doc.setRootElement(root);
		obj2Element(obj, root, dateFormat);
		return doc;
	}

	public static Document obj2xml(Object obj) {
		return obj2xml(obj, DATEFORMAT);
	}

	public static void obj2Element(Object obj, Element el, String dateFormat) {
		if (obj instanceof java.lang.Boolean) {
			el.setText(obj.toString());
		} else if (obj instanceof java.lang.Integer) {
			el.setText(obj.toString());
		} else if (obj instanceof java.lang.Float) {
			el.setText(String.valueOf(obj));
		} else if (obj instanceof java.lang.Double) {
			el.setText(String.valueOf(obj));
		} else if (obj instanceof java.lang.Number) {
			el.setText(String.valueOf(obj));
		} else if (obj instanceof java.util.Date) {
			SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
			el.setText(sdf.format((Date) obj));
		} else if (obj instanceof java.lang.String) {
			el.setText((String) obj);
		} else if (obj instanceof java.util.List) {
			List<?> values = (List<?>) obj;
			for (Object value : values) {
				if (value instanceof java.util.List) {
					Log.INSTANCE.error("List value of el:{} can not be List", el.getName());
				} else {
					obj2Element(value, el, dateFormat);
				}
			}
		} else if (obj instanceof java.util.Map) {
			@SuppressWarnings("unchecked")
			Map<String, Object> map = (Map<String, Object>) obj;
			Iterator<String> iterator = map.keySet().iterator();
			while (iterator.hasNext()) {
				String key = iterator.next();
				Element addElement = el.addElement(key);
				Object value = map.get(key);
				obj2Element(value, addElement, dateFormat);
			}
		} else {
			try {
				pojo2Element(obj, el, dateFormat);
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
	}

	public static void obj2Element(Object obj, Element e) {
		obj2Element(obj, e, DATEFORMAT);
	}

	public static void pojo2Element(Object pojo, Element e, String dateFormat) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		Method methods[] = pojo.getClass().getMethods();
		for (Method method : methods) {
			String name = method.getName();
			if (name.startsWith("get")) {
				Object value = method.invoke(pojo, new Object[] {});
				obj2Element(value, e, dateFormat);
			}
		}
	}

	/**
	 * @see 此方法假设 元素下如果是List元素 则全部是同名List元素
	 * @param el
	 * @param parentMap
	 */
	public static void element2Map(Element el, Map<String, Object> parentMap, boolean islist) {
		element2Map(el, parentMap, islist, false);
	}

	/**
	 * @see 此方法假设 元素下如果是List元素 则全部是同名List元素
	 * @param el
	 * @param parentMap
	 */
	@SuppressWarnings("unchecked")
	public static void element2Map(Element el, Map<String, Object> parentMap, boolean islist, boolean caseSensitive) {
		String name = el.getName();
		if (caseSensitive) {
			name = name.toLowerCase();
		}
		List<Element> elements = el.elements();
		if (elements.size() > 0) {// 有子元素
			// 查询同名元素
			List<Element> sames = el.elements(elements.get(0).getName());
			if (sames.size() > 1) {// 多个同名元素
				ArrayList<Map<String, Object>> arrayList = new ArrayList<Map<String, Object>>();
				parentMap.put(name, arrayList);
				for (Element element : sames) {
					Map<String, Object> hashMap = new HashMap<String, Object>();
					arrayList.add(hashMap);
					element2Map(element, hashMap, true, caseSensitive);
				}
			} else {
				Map<String, Object> hashMap = null;
				if (islist) {
					hashMap = parentMap;
				} else {
					hashMap = new HashMap<String, Object>();
					parentMap.put(name, hashMap);
				}
				for (Element element : elements) {
					element2Map(element, hashMap, false, caseSensitive);
				}
			}
		} else {
			parentMap.put(name, el.getText());
		}
	}

	public static Object element2Bean(Element el) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		String className = el.attributeValue("class");
		Class<?> clazz = Class.forName(className);
		Object newInstance = clazz.newInstance();
		Method methods[] = clazz.getMethods();
		for (Method method : methods) {
			String name = method.getName();
			if (name.startsWith("set")) {
				@SuppressWarnings("unchecked")
				List<Element> list = el.elements("property");
				for (Element e : list) {
					String feildName = e.attributeValue("name");
					String keyMethodName = new StringBuffer("set").append(feildName).toString();
					if (keyMethodName.toUpperCase().equals(name.toUpperCase())) {
						method.invoke(newInstance, new Object[] { e.attributeValue("value") });
					}
				}
			}
		}
		return newInstance;
	}

	public static Document fileToDoc(File xml) throws DocumentException, IOException {
		try (FileInputStream inputStream = new FileInputStream(xml);) {
			return inputStreamToDoc(inputStream);
		}
	}

	public static Document fileToDoc(String path) throws DocumentException, IOException {
		return fileToDoc(new File(path));
	}

	public static Document inputStreamToDoc(InputStream inputStream, String charset) {
		Document doc = null;
		InputStreamReader reader = null;
		try {
			if (charset != null) {
				reader = new InputStreamReader(inputStream, charset);
			} else {
				reader = new InputStreamReader(inputStream);
			}
			doc = readerToDoc(reader);
		} catch (UnsupportedEncodingException | DocumentException e) {
			Log.INSTANCE.error("inputStream To Doc error", e);
		} finally {
			IOUtil.flushAndCloseQuietly(reader);
		}
		return doc;
	}

	public static Document inputStreamToDoc(InputStream inputStream) throws UnsupportedEncodingException, DocumentException {
		return inputStreamToDoc(inputStream, null);
	}

	public static Document stringToDoc(String xml) throws DocumentException {
		return DocumentHelper.parseText(xml);
	}

	public static Document readerToDoc(Reader reader) throws DocumentException {
		Document doc = new SAXReader().read(reader);
		return doc;
	}

	public static void outputStreamDocFormated(String xml, OutputStream outputStream) throws IOException, DocumentException {
		outputStreamDocFormated(stringToDoc(xml), outputStream);
	}

	public static void outputStreamDocFormated(Document doc, OutputStream outputStream) throws IOException {
		OutputFormat formater = OutputFormat.createPrettyPrint();

		formater.setEncoding(doc.getXMLEncoding());

		XMLWriter writer = new XMLWriter(outputStream, formater);
		writer.write(doc);
	}

}
